#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/UefiBootServicesTableLib.h>
#include  <Protocol/GraphicsOutput.h>
#include  <Library/MemoryAllocationLib.h>

#define FONT_HEIGHT 55
#define FONT_WIDTH 60
#define FONT_ARRAY_SIZE 440
#define FONT_LINE_COUNT (FONT_ARRAY_SIZE / FONT_HEIGHT)
#define FONT_COUNT 4

extern unsigned char bits[][440];

VOID DrawByte(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 Byte,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i;

    for (i = 0; i < 8; i ++) {
        if (Byte & 0x80) {
            GraphicsOutput->Blt(GraphicsOutput, &Color, EfiBltVideoFill, 0, 0, X, Y, 1, 1, 0);
        }
        /* 从高位到低位逐个取值 */
        Byte <<= 1; 
        X ++;
    }
}

VOID DrawFont(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 *FontBitMap, 
    UINT8 Height, 
    UINT8 LineCout,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i, j;
    UINTN BackupX;

    BackupX = X;

    for(i = 0; i < Height; i ++) {
        X = BackupX;
        /* 画一行，每行8个byte */
        for(j = 0; j < LineCout; j ++) {
            DrawByte(GraphicsOutput, FontBitMap[i * LineCout + j], X, Y, Color);
            X += LineCout;
        }
        Y ++;
    }
}

VOID DrawFontBuffer(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 *FontBitMap, 
    UINT8 Height, 
    UINT8 Width,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color    
)
{
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *FontBuf;
    UINTN i, k;
    UINTN BitIndex, LineBit;

    BitIndex = 0;
    LineBit  = 0;

    FontBuf = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocateZeroPool (Height * Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    
    for (i = 0; i < FONT_ARRAY_SIZE; i ++) {
        for (k = 0; k < 8; k ++) {
            if ((FontBitMap[i] << k) & 0x80) {
                FontBuf[BitIndex] = Color;
            }
            BitIndex ++;

            if (++ LineBit == Width) {
                LineBit = 0;
                break;
            }
        }
    }
    GraphicsOutput->Blt(GraphicsOutput, FontBuf, EfiBltBufferToVideo, 0, 0, X, Y, Width, Height, 0);
}

int main (
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS Status;
    UINTN ScreenWidth, ScreenHeight, i;
    EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorWhite = { 0xFF, 0xFF, 0xFF, 0 };
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorBlue  = { 0xFF, 0x00, 0x00, 0 };

    Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
        
    if (EFI_ERROR (Status)) {
        return EFI_UNSUPPORTED;
    }

    ScreenWidth  = GraphicsOutput->Mode->Info->HorizontalResolution;
    ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;

    /* 将屏幕填充为白色 */
    Status = GraphicsOutput->Blt(
                    GraphicsOutput,
                    &ColorWhite,
                    EfiBltVideoFill,
                    0, 0,
                    0, 0, 
                    ScreenWidth, ScreenHeight,   
                    0
                    );

    for (i = 0; i < FONT_COUNT; i ++) {
        //DrawFont(GraphicsOutput, bits[i], FONT_HEIGHT, FONT_LINE_COUNT, 100 + FONT_WIDTH * i, 100, ColorBlue);
        DrawFontBuffer(GraphicsOutput, bits[i], FONT_HEIGHT, FONT_WIDTH, 100 + FONT_WIDTH * i, 100, ColorBlue);
    }

    return(0);
}
